#pragma once 

#include <iostream>
#include <string>
#include <cstring>
#include <errno.h>
#include <cstdlib>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include "InetAddr.hpp"
#include "Log.hpp"

using fun_t = std::function<std::string(const std::string&, bool&)>;

enum
{
    SOCKET_ERROR = 1,
    BIND_ERROR,
    USAGE_ERROR,
};

const static int defaultfd = -1;

class UdpServer
{
public:
    UdpServer(uint16_t port,  fun_t fun)
        : _port(port), _fun(fun), _isrunning(false), _fd(defaultfd)
    {
    }
    void Init()
    {
        // 1. 创建udp socket套接字
        _fd = socket(AF_INET, SOCK_DGRAM, 0); // AF_INET:Ipv4协议的网络通信  SOCK_DGRAM:面向数据报(udp)
        if (_fd < 0)
        {
            LOG(FATAL, "socket error, %s, %d", strerror(errno), errno);
            exit(SOCKET_ERROR);
        }
        LOG(INFO, "socket create success...");

        // 2. 填充sockaddr_in结构
        struct sockaddr_in local;   
        bzero(&local, sizeof(local));   //初始化为0
        local.sin_family = AF_INET;     //网络通信字段 - 结构体的首部
        local.sin_port = htons(_port);  //端口号 - 转成网络序列(大端)
        //in_addr_t inet_addr(const char *cp); 1.将字符串风格ip地址转化为4字节ip地址 2.将4字节ip地址转化成网络序列
        //local.sin_addr.s_addr = inet_addr(_ip.c_str());
        local.sin_addr.s_addr = INADDR_ANY; //((in_addr_t) 0x00000000)

        //3. 绑定sockfd和server网络信息     将来client通过server网络信息访问server
        int n = bind(_fd, (sockaddr*)&local, sizeof(local));
        if (n < 0)
        {
            LOG(FATAL, "bind error, %s, %d", strerror(errno), errno);
            exit(BIND_ERROR);
        }
        LOG(INFO, "socket bind success...");
    }

    void Start()
    {
        _isrunning = true;
        while (_isrunning)
        {
            //1. 从client收消息
            char quest[1024];  //输出参数：存储读取的消息
            struct sockaddr_in peer;    //记录谁向本机器发送消息
            socklen_t len = sizeof(peer);   //必须初始化为sizeof(peer)
            bzero(&peer, 0);
            int n = recvfrom(_fd, quest, 1023, 0, (struct sockaddr*)&peer, &len);
            if (n > 0)
            {
                //成功收到消息
                InetAddr addr(peer);
                quest[n] = 0;
                LOG(INFO, "get a message: %s from [%s:%d]", quest, addr.Ip().c_str(), addr.Port());
                // 进行业务处理
                bool ok = false;
               std::string response = _fun(quest, ok);    //调用业务处理模块
               (void)(ok);

                // 将处理完的结果发回给client
                sendto(_fd, response.c_str(), response.size(), 0, (sockaddr*)&peer, len);
            }
        }
    }
    ~UdpServer()
    {}
private:
    int _fd;         // 网卡对应的文件描述符
    // std::string _ip; // 服务器的ip地址绑定为0。表示绑定当前机器中的任意ip 客户端可以通过不同的ip地址访问服务器 比如可以通过服务器的公网ip也可以通过本地ip和内网ip
    uint16_t _port;  // server对应的端口号
    bool _isrunning; // 是否启动server
    fun_t _fun;     //服务器回调，注册业务逻辑
};